package com.manger.common.office.sax;

import com.manger.common.office.sax.export.SXSSFSheetRowHandle;
import com.manger.common.office.sax.utils.TargetClassParse;
import com.manger.common.office.sax.utils.UserTargetModel;
import com.manger.utils.ClassUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class SXSSFExportExecutor {

    private static final int BATCH_SIZE = 100;

    private SXSSFWorkbook wb;

    private List<String> sheetNameList = new ArrayList<>();

    private List<Class<?>> classList = new ArrayList<>();

    private List<List<?>> dataList = new ArrayList<>();

    private Map<String,Integer> sheetLastRowIndexxMap = new HashMap<>();

    private SXSSFSheetRowHandle sxssfSheetRowHandle = new SXSSFSheetRowHandle() {
        @Override
        public void begin(String sheetName, Object object, int rowNum) {
            super.begin(sheetName, object, rowNum);
        }

        @Override
        public void end(String sheetName, Object object, int rowNum) {
            super.end(sheetName, object, rowNum);
        }

    };

    protected SXSSFExportExecutor() {

    }

    public SXSSFExportExecutor listen(SXSSFSheetRowHandle sxssfSheetRowHandle){
        this.sxssfSheetRowHandle = sxssfSheetRowHandle;
        return this;
    }

    public SXSSFExportExecutor sheet(String sheetName) {
        sheetNameList.add(sheetName);
        return this;
    }

    public SXSSFExportExecutor head(Class<?> claz) {
        classList.add(claz);
        return this;
    }

    public SXSSFExportExecutor data(List<?> data) {
        dataList.add(data);
        return this;
    }

    public SXSSFExportExecutor data(Object rowData) {
        List<Object> temp = new ArrayList<>();
        temp.add(rowData);
        dataList.add(temp);
        return this;
    }
    /**
     * 推荐小文件使用
     * */
    public SXSSFExportExecutor append(FileInputStream inputStream) throws Exception{
        check();
        if(inputStream.available() <= 0){
            return this;
        }
        createSXSSFWorkbook(inputStream);
        return this;
    }

    public void write(OutputStream outputStream) throws Exception {

        check();

        long start = System.currentTimeMillis();

        if(wb == null){
            createSXSSFWorkbook();
        }

        SXSSFCell sxssfCell ;
        SXSSFRow sxssfRow;
        Object dic;
        int lastRow = 0;
        for (int i = 0; i < sheetNameList.size(); i++) {
            lastRow = 0;
            String sheetName = sheetNameList.get(i);
            Class<?> claz = classList.get(i);
            List<?> list = dataList.get(i);
            SXSSFSheet sheet =  wb.getSheet(sheetName) ;
            if(sheet == null){
                sheet = wb.createSheet(sheetName);
            }
            List<UserTargetModel> userTargetModelList = TargetClassParse.parse(claz);
            //过滤不导出字段
            userTargetModelList.stream().filter(userTargetModel -> userTargetModel.getExcel().export().isExport() == true).collect(Collectors.toList());
            if(!sheetLastRowIndexxMap.containsKey(sheetName)){
                createTitle(userTargetModelList, sheet.createRow(0));
            }else {
                lastRow = sheetLastRowIndexxMap.get(sheetName);
            }
            for (int j = 0; j < list.size(); j++) {
                sxssfRow = sheet.createRow(lastRow + j + 1);
                Object object = list.get(j);
                sxssfSheetRowHandle.begin(sheetName,object,j+1);
                for (int c = 0; c < userTargetModelList.size(); c++) {
                    UserTargetModel userTargetModel = userTargetModelList.get(c);
                    Object o = ClassUtils.invokeGet(object, userTargetModel.getField());
                    String value =  o == null ? "" : String.valueOf(o);

                    if (!userTargetModel.getDicMap().keySet().isEmpty()) {
                        dic = userTargetModel.getDicMap().get(value);
                        value = dic == null ? "" : String.valueOf(dic);
                    }

                    sxssfCell = sxssfRow.createCell(c);
                    sxssfCell.setCellValue(value);
                }
                sxssfSheetRowHandle.end(sheetName,object,j+1);
            }

        }

        wb.write(outputStream);
        outputStream.flush();
        outputStream.close();
        wb.dispose();

        System.out.println("导出耗时：" + (System.currentTimeMillis() - start));

    }

    public void multiThreadWrite(OutputStream outputStream) throws Exception {

        check();

        long start = System.currentTimeMillis();

        createSXSSFWorkbook();

        List<CompletableFuture> completableFutureList = new ArrayList<>();

        for (int i = 0; i < sheetNameList.size(); i++) {

            String sheetName = sheetNameList.get(i);
            Class<?> claz = classList.get(i);
            List<?> list = dataList.get(i);

            SXSSFSheet sheet = wb.createSheet(sheetName);
            List<UserTargetModel> userTargetModelList = TargetClassParse.parse(claz);

            CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {

                try {
                    createTitle(userTargetModelList, sheet.createRow(0));
                    SXSSFCell sxssfCell ;
                    SXSSFRow sxssfRow;
                    Object dic;
                    for (int j = 0; j < list.size(); j++) {
                        sxssfRow = sheet.createRow(j + 1);
                        Object object = list.get(j);
                        sxssfSheetRowHandle.begin(sheetName,object,j+1);
                        for (int c = 0; c < userTargetModelList.size(); c++) {
                            UserTargetModel userTargetModel = userTargetModelList.get(c);
                            Object o = ClassUtils.invokeGet(object, userTargetModel.getField());
                            String value =  o == null ? "" : String.valueOf(o);

                            if (!userTargetModel.getDicMap().keySet().isEmpty()) {
                                dic = userTargetModel.getDicMap().get(value);
                                value = dic == null ? "" : String.valueOf(dic);
                            }

                            sxssfCell = sxssfRow.createCell(c);
                            sxssfCell.setCellValue(value);
                        }
                        sxssfSheetRowHandle.end(sheetName,object,j+1);
                    }
                    return "";

                }catch (Exception e){
                    e.printStackTrace();
                    return e.getMessage();
                }

            });
            completableFutureList.add(completableFuture);
        }
        for(CompletableFuture<String> completableFuture: completableFutureList){
            String error = completableFuture.get();
            if(StringUtils.isNotEmpty(error)){
                throw new Exception(error);
            }
        }
        long l = System.currentTimeMillis();

        wb.write(outputStream);
        outputStream.close();
        wb.dispose();
        wb.close();

        System.out.println("导出耗时：" + (System.currentTimeMillis() - start));

    }

    private void createSXSSFWorkbook(){
        wb = new SXSSFWorkbook(100);
        wb.setCompressTempFiles(true);
    }

    private void createSXSSFWorkbook(FileInputStream fileInputStream) throws Exception{
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook(fileInputStream);
        Iterator<Sheet> iterator =  xssfWorkbook.sheetIterator();
        while (iterator.hasNext()){
            Sheet sheet = iterator.next();
            sheetLastRowIndexxMap.put(sheet.getSheetName(), sheet.getLastRowNum());
        }
        wb = new SXSSFWorkbook(xssfWorkbook,100);
        wb.setCompressTempFiles(true);
    }

    private void check() throws Exception {

        if (sheetNameList.size() != classList.size() || sheetNameList.size() <= 0 || classList.size() <= 0) {
            throw new Exception("请设置正确的sheet名称与对象的class!");
        }

        if (dataList.size() != classList.size()) {
            throw new Exception("请设置与sheet长度相同的数据集!");
        }

    }

    private void createTitle(List<UserTargetModel> list, SXSSFRow row) {
        for (int i = 0; i < list.size(); i++) {
            UserTargetModel userTargetModel = list.get(i);
            SXSSFCell cell = row.createCell(i);
            cell.setCellValue(userTargetModel.getExcel().desc());
        }
    }
}
